1 Introduction

The glatos package includes several functions that facilitate the creation of animated videos of animal movements from acoustic telemetry data. The primary functions for building animated videos are the interpolate_path and make_frames functions. interpolate_path calculates interpolated positions of animals between detections and make_frames creates a series of snapshots of interpolated and detected fish locations over time from the output of interpolate_path. In addition to creating and outputting frames, make_frames can also stitch frames together to create an animated video of movements using the open source FFmpeg software. Although make_frames was developed to allow customization of the output content and layout of frames, some may find the level of customization to be insufficient for their needs. In this vignette, we present a short example of an alternative approach for creating animations using the ggplot2 and gganimate R packages. 1 ggplot2 is a popular and powerful package for plotting figures based on The Grammer of Graphics approach to visualizing data and developed for R by Hadley Wickham. 2 ggplot2 differs from other plotting functions in R by its layered, interactive approach to building graphical output. Variables in data sets are mapped to aesthetic categories used to describe and build the plot. This approach differs from other packages in R that require the structure and layout of the plot to generally be specified simultaneously. The gganimate package is an extension to ggplot2 that provides support for animations 3. In the following code, we use the make_transition function on an example dataset included in glatos to create a custom transition layer necessary for interpolating fish movements. We then use the interpolate_path function in glatos to interpolate fish positions in space and time 4.Finally, we end the vignette with a demonstration of code to create an animation of fish movements using ggplot2 and gganimate.

In the first chunk we load necessary packages, obtain example data from glatos, clean up the data, and create a figure of lakes Huron and Erie (Figure 1). The data.table R package is used for data manipulations in this vignette. 5

library(glatos)
library(sp)
library(sf)
library(raster)
library(gganimate)
library(ggplot2)
library(data.table)

# get polygon of the Great Lakes
data(greatLakesPoly)

# convert to sf spatial object
lakes <- st_as_sf(greatLakesPoly)

# crop polygon to extract Lake Huron and Lake Erie
lakes <- st_crop(lakes, xmin = -84.8, xmax = -79.6, ymin = 41.3, ymax = 46.6)
plot(st_geometry(lakes), col = "grey")
Lakes Huron and Erie extracted from Great Lakes polygon included in the `glatos` package

Lakes Huron and Erie extracted from Great Lakes polygon included in the glatos package

# get example walleye detection data
det_file <- system.file("extdata", "walleye_detections.csv", package = "glatos")
det <- read_glatos_detections(det_file)

# bring in receiver file and convert to sf spatial object
rec_file <- system.file("extdata", "sample_receivers.csv", package = "glatos")
recs <- read_glatos_receivers(rec_file)
setDT(recs)
recs <- recs[deploy_long < -80, ]
recs_sf <- st_as_sf(recs, coords = c("deploy_long", "deploy_lat"), crs = 4326)

In the next bit of code, we create a transition layer from lakes. The transition layer is needed to calculate non-linear interpolated movement paths that avoid impossible overland movements of fish. We set the all_touched argument = TRUE to allow any pixel that touches the polygon outline of the Great Lakes to be coded as water in the resulting transition layer. If all_touched = FALSE, then more than half of the pixel must be in the polygon in order to be coded as water.

# temporarily convert sf spatial to sp make_transition only accepts sp
# SpatialPolygonsDataFrame
lakes <- as(lakes, "Spatial")

# make transition layer.
tran <- make_transition(lakes, res = c(0.01, 0.01), all_touched = TRUE)

# convert back to sf
lakes <- st_as_sf(lakes)
# create quick plot of fish and receivers to make sure they are 'on the map'
# (Figure 2)
unique_fish <- unique(det, by = c("deploy_lat", "deploy_long"))
plot(tran$rast)
plot(st_geometry(lakes), add = TRUE)
plot(st_geometry(recs_sf), pch = 20, col = "orange", add = TRUE)
points(unique_fish$deploy_long, unique_fish$deploy_lat, pch = 20, col = "red")
Plot of transition layer, receivers (orange circles), fish detection location (red circles) in lakes Huron and Erie.

Plot of transition layer, receivers (orange circles), fish detection location (red circles) in lakes Huron and Erie.

Next we interpolate detection data and do some cleanup.

# interpolate detections.
pos2 <- interpolate_path(det, trans = tran$transition, lnl_thresh = 0.99)
# convert pos2 to data.table
setDT(pos2)

# extract unique bin_timestamp
int <- unique(pos2, by = "bin_timestamp")

In the next code chunk, we use a data.table join to add the bin_timestamp output in the interpolated positions (pos2) to the receiver object. We use receiver deployment and recovery timestamps to match with bin_timestamp. This is so we can display receiver deployment and recovery in the animation. We essentially need to know what receivers were deployed during each bin_timestamp interval.

# Add bin_timestamp to receivers, based on deploy/recover timestamps.
# Removes unnecessary columns in output to simplify
recs <- recs[int, .(deploy_lat = x.deploy_lat, deploy_long = x.deploy_long, 
    station = x.station, deploy_date_time = x.deploy_date_time, recover_date_time = x.recover_date_time, 
    bin_timestamp = i.bin_timestamp), on = .(deploy_date_time <= bin_timestamp, 
    recover_date_time >= bin_timestamp), allow.cartesian = TRUE]

We are ready to create the animation. The first step is to create a static image of all receivers, detections, and interpolated detections with ggplot2. We changed the color of fish detections to red circles, interpolated positions to blue circles and made detections larger than interpolated detections.

ggplot(data = lakes) + geom_sf(color = "black", fill = "lightgrey") + geom_point(data = recs, 
    aes(x = deploy_long, y = deploy_lat), size = 2, color = "orange", inherit.aes = FALSE) + 
    geom_point(data = pos2, aes(x = longitude, y = latitude, group = animal_id, 
        color = record_type, size = record_type), inherit.aes = FALSE) + xlab("Longitude") + 
    ylab("Latitude") + scale_color_manual(values = c("red", "blue")) + scale_size_manual(values = c(2, 
    1))
Static figure of fish detections (red), interpolated positions (blue), and receivers (orange) plotted using `ggplot2`

Static figure of fish detections (red), interpolated positions (blue), and receivers (orange) plotted using ggplot2

Now we will animate the plot with gganimate

ggplot(data = lakes) +
  geom_sf(color = "black", fill = "lightgrey") +
  geom_point(data = recs, aes(x = deploy_long, y = deploy_lat),
             size = 2, color = "orange", inherit.aes = FALSE) +
  geom_point(data=pos2, aes(x=longitude, y=latitude,
                            group=animal_id, color=record_type,
                            size=record_type), inherit.aes=FALSE) +
  xlab("Longitude") +
  ylab("Latitude") +
  scale_color_manual(values=c("red", "blue"))+
  scale_size_manual(values=c(2,1)) +
  transition_time(bin_timestamp) +
  ggtitle('{frame_time}')
## 
Frame 1 (1%)
Frame 2 (2%)
Frame 3 (3%)
Frame 4 (4%)
Frame 5 (5%)
Frame 6 (6%)
Frame 7 (7%)
Frame 8 (8%)
Frame 9 (9%)
Frame 10 (10%)
Frame 11 (11%)
Frame 12 (12%)
Frame 13 (13%)
Frame 14 (14%)
Frame 15 (15%)
Frame 16 (16%)
Frame 17 (17%)
Frame 18 (18%)
Frame 19 (19%)
Frame 20 (20%)
Frame 21 (21%)
Frame 22 (22%)
Frame 23 (23%)
Frame 24 (24%)
Frame 25 (25%)
Frame 26 (26%)
Frame 27 (27%)
Frame 28 (28%)
Frame 29 (29%)
Frame 30 (30%)
Frame 31 (31%)
Frame 32 (32%)
Frame 33 (33%)
Frame 34 (34%)
Frame 35 (35%)
Frame 36 (36%)
Frame 37 (37%)
Frame 38 (38%)
Frame 39 (39%)
Frame 40 (40%)
Frame 41 (41%)
Frame 42 (42%)
Frame 43 (43%)
Frame 44 (44%)
Frame 45 (45%)
Frame 46 (46%)
Frame 47 (47%)
Frame 48 (48%)
Frame 49 (49%)
Frame 50 (50%)
Frame 51 (51%)
Frame 52 (52%)
Frame 53 (53%)
Frame 54 (54%)
Frame 55 (55%)
Frame 56 (56%)
Frame 57 (57%)
Frame 58 (58%)
Frame 59 (59%)
Frame 60 (60%)
Frame 61 (61%)
Frame 62 (62%)
Frame 63 (63%)
Frame 64 (64%)
Frame 65 (65%)
Frame 66 (66%)
Frame 67 (67%)
Frame 68 (68%)
Frame 69 (69%)
Frame 70 (70%)
Frame 71 (71%)
Frame 72 (72%)
Frame 73 (73%)
Frame 74 (74%)
Frame 75 (75%)
Frame 76 (76%)
Frame 77 (77%)
Frame 78 (78%)
Frame 79 (79%)
Frame 80 (80%)
Frame 81 (81%)
Frame 82 (82%)
Frame 83 (83%)
Frame 84 (84%)
Frame 85 (85%)
Frame 86 (86%)
Frame 87 (87%)
Frame 88 (88%)
Frame 89 (89%)
Frame 90 (90%)
Frame 91 (91%)
Frame 92 (92%)
Frame 93 (93%)
Frame 94 (94%)
Frame 95 (95%)
Frame 96 (96%)
Frame 97 (97%)
Frame 98 (98%)
Frame 99 (99%)
Frame 100 (100%)
## Finalizing encoding... done!
Animated figure of fish movements in lakes Huron and Erie. Only last image of animation is included in this vignette

Animated figure of fish movements in lakes Huron and Erie. Only last image of animation is included in this vignette

Notice the addition of a transition_time and ggtitle component in the code above. The resulting animation may be customized by specifying arguments to the animate function. Please see ?animate for more information. Also, specifying anim_save may be used to write animations to disk.

This vignette provides a basic, high-level summary of creating an animated video with ggplot2 and gganimate. These packages provides much more functionality than included in this vignette. Please refer to documentation for gganimate and ggplot2 for additional examples and customization that may be useful for visualizing fish movements.


  1. For more information about obtaining gganmiate and ggplot2, see https://gganimate.com/ and https://ggplot2.tidyverse.org/

  2. Leland Wilkinson,https://www.amazon.com/Grammar-Graphics-Statistics-Computing/dp/0387245448

  3. see https://gganimate.com/articles/gganimate.html

  4. See glatos package help for more information about functional arguments and outputs of make_frames and interpolate_path. Additional details about creating animated videos of fish movements using the glatos package is found in the workshop manual “A guide to R for analyzing Acoustic Telemetry Data V2.0”. A pdf of manual can be downloaded here https://gitlab.oceantrack.org/GreatLakes/glatos/wikis/Past%20r%20workshops%20and%20manuals

  5. Data.table information: https://github.com/Rdatatable/data.table/wiki